package org.asiainfo.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.tree.Tree;
import com.mybatisflex.core.query.QueryMethods;
import org.asiainfo.common.core.constant.CacheNames;
import org.asiainfo.common.core.constant.UserConstants;
import org.asiainfo.common.core.exception.ServiceException;
import org.asiainfo.common.core.utils.SpringUtils;
import org.asiainfo.common.core.utils.StringUtils;
import org.asiainfo.common.core.utils.TreeBuildUtils;
import org.asiainfo.common.mybatis.stream.utils.DataScopeUtils;
import org.asiainfo.common.redis.utils.CacheUtils;
import org.asiainfo.common.satoken.utils.LoginHelper;
import org.asiainfo.system.domain.SysRole;
import org.asiainfo.system.domain.SysRoleDept;
import org.asiainfo.system.domain.SysUser;
import org.asiainfo.system.domain.table.SysDeptTableDef;
import org.asiainfo.system.domain.table.SysRoleDeptTableDef;
import org.asiainfo.system.domain.vo.SysDeptVo;
import org.asiainfo.system.domain.bo.SysDeptBo;
import org.asiainfo.system.domain.SysDept;
import org.asiainfo.system.mapper.SysRoleMapper;
import org.asiainfo.system.mapper.SysUserMapper;
import org.asiainfo.system.service.SysDeptService;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.asiainfo.common.core.utils.MapstructUtils;
import lombok.extern.slf4j.Slf4j;
import cn.hutool.core.util.ObjectUtil;
import com.mybatisflex.core.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.asiainfo.system.mapper.SysDeptMapper;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 部门信息表(SysDept)表服务实现类
 *
 * @author dotor-ww
 * @since 2024-01-20 22:05:32
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> implements SysDeptService {

    private final SysRoleMapper sysRoleMapper;

    private final SysUserMapper sysUserMapper;

    /**
     * 构建查询条件
     *
     * @param sysDeptBo 筛选条件
     * @return 查询结果
     */
    private QueryWrapper buildQueryWrapper(SysDeptBo sysDeptBo) {
        QueryWrapper query = query();
        query.eq(SysDept::getDeptId, sysDeptBo.getDeptId(), ObjectUtil.isNotNull(sysDeptBo.getDeptId()));
        query.eq(SysDept::getParentId, sysDeptBo.getParentId(), ObjectUtil.isNotNull(sysDeptBo.getParentId()));
        query.likeLeft(SysDept::getDeptName, sysDeptBo.getDeptName(), StringUtils.isNotBlank(sysDeptBo.getDeptName()));
        query.eq(SysDept::getDeptStatus, sysDeptBo.getDeptStatus(), StringUtils.isNotBlank(sysDeptBo.getDeptStatus()));
        query.orderBy(SysDept::getParentId);
        query.orderBy(SysDept::getDeptId);
        return query;
    }

    /**
     * 查询
     *
     * @param sysDeptBo 筛选条件
     * @return 查询结果
     */
    @Override
    public List<SysDeptVo> queryList(SysDeptBo sysDeptBo) {
        QueryWrapper query = DataScopeUtils.addCondition(buildQueryWrapper(sysDeptBo));
        return listAs(query, SysDeptVo.class);
    }

    /**
     * 通过deptId查询单条数据
     *
     * @param deptId 主键
     * @return 实例对象
     */
    @Cacheable(cacheNames = CacheNames.SYS_DEPT, key = "#deptId")
    @Override
    public SysDeptVo queryById(Long deptId) {
        SysDeptVo sysDeptVo = getOneAs(query().eq(SysDept::getDeptId, deptId), SysDeptVo.class);
        if (ObjectUtil.isNull(sysDeptVo)) {
            return sysDeptVo;
        }
        SysDeptVo parentDeptVo = getOneAs(query().eq(SysDept::getDeptId, sysDeptVo.getParentId()), SysDeptVo.class);
        sysDeptVo.setParentName(ObjectUtil.isNotNull(parentDeptVo) ? parentDeptVo.getDeptName() : null);
        return sysDeptVo;
    }

    /**
     * 新增数据
     *
     * @param sysDeptBo 实例对象
     * @return 实例对象
     */
    @Override
    public Boolean insertByBo(SysDeptBo sysDeptBo) {
        SysDept info = getById(sysDeptBo.getParentId());
        if (!UserConstants.DEPT_NORMAL.equals(info.getDeptStatus())) {
            throw new ServiceException("部门停用，不允许新增");
        }
        SysDept sysDept = MapstructUtils.convert(sysDeptBo, SysDept.class);
        if (ObjectUtil.isNull(sysDept)) {
            throw new ServiceException("部门对象不能为空");
        }
        sysDept.setAncestors(info.getAncestors() + StringUtils.SEPARATOR + sysDept.getParentId());
        return save(sysDept);
    }

    /**
     * 修改数据
     *
     * @param sysDeptBo 实例对象
     * @return 实例对象
     */
    @CacheEvict(cacheNames = CacheNames.SYS_DEPT, key = "#sysDeptBo.deptId")
    @Override
    public Boolean updateByBo(SysDeptBo sysDeptBo) {
        SysDept sysDept = MapstructUtils.convert(sysDeptBo, SysDept.class);
        if (ObjectUtil.isNull(sysDept)) {
            throw new ServiceException("未找到部门信息");
        }
        SysDept oldDept = getById(sysDept.getDeptId());
        if (sysDept.getParentId().equals(oldDept.getParentId())) {
            checkDeptDataScope(sysDept.getParentId());
            SysDept newParentDept = getById(sysDept.getParentId());
            if (ObjectUtil.isNotNull(newParentDept) && ObjectUtil.isNotNull(oldDept)) {
                String newAncestors = newParentDept.getAncestors() + StringUtils.SEPARATOR + newParentDept.getDeptId();
                String oldAncestors = oldDept.getAncestors();
                sysDept.setAncestors(newAncestors);
                updateDeptChildren(sysDept.getDeptId(), newAncestors, oldAncestors);
            }
        }
        if (UserConstants.DEPT_NORMAL.equals(sysDept.getDeptStatus()) && StringUtils.isNotEmpty(sysDept.getAncestors())
            && !StringUtils.equals(UserConstants.DEPT_NORMAL, sysDept.getAncestors())) {
            updateParentDeptStatusNormal(sysDept);
        }
        return updateById(sysDept);
    }

    /**
     * 递归更新父级部门状态
     *
     * @param deptId       部门ID
     * @param newAncestors 新部门ancestors
     * @param oldAncestors 旧部门ancestors
     */
    private void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors) {

        List<SysDept> children = list(
            QueryMethods.findInSet(QueryMethods.number(deptId), SysDeptTableDef.SYS_DEPT.ANCESTORS)
                .gt(0));

        List<SysDept> list = children.stream().map(s -> {
            SysDept sysDept = new SysDept();
            sysDept.setDeptId(s.getDeptId());
            sysDept.setAncestors(s.getAncestors().replaceFirst(oldAncestors, newAncestors));
            return sysDept;
        }).toList();

        if (CollUtil.isNotEmpty(list)) {
            if (updateBatch(list)) {
                list.forEach(dept -> CacheUtils.evict(CacheNames.SYS_DEPT, dept.getDeptId()));
            }
        }
    }

    /**
     * 修改部门的父级部门状态
     *
     * @param sysDept 部门信息
     */
    private void updateParentDeptStatusNormal(SysDept sysDept) {
        String ancestors = sysDept.getAncestors();
        Long[] deptIds = Convert.toLongArray(ancestors);
        updateChain().set(SysDept::getDeptStatus, UserConstants.DEPT_NORMAL)
            .in(SysDept::getDeptId, Arrays.asList(deptIds));
    }

    /**
     * 通过主键删除数据
     *
     * @param deptId 主键
     * @return 是否成功
     */
    @CacheEvict(cacheNames = CacheNames.SYS_DEPT, key = "#deptId")
    @Override
    public Boolean deleteWithValidById(Long deptId) {
        return removeById(deptId);
    }

    /**
     * 校验部门是否有数据权限
     *
     * @param deptId 部门ID
     */
    @Override
    public void checkDeptDataScope(Long deptId) {
        if (ObjectUtil.isNull(deptId)) {
            return;
        }
        if (LoginHelper.isSuperAdmin()) {
            return;
        }
        QueryWrapper query = DataScopeUtils.addCondition(query());
        query.eq(SysDept::getDeptId, deptId, ObjectUtil.isNotNull(deptId));
        if (count(query) == 0) {
            throw new ServiceException("没有权限访问部门数据");
        }
    }

    /**
     * 角色ID查询部门树信息
     *
     * @param roleId 角色ID
     * @return 查询结果
     */
    @Override
    public List<Long> selectDeptIdsByRoleId(Long roleId) {
        SysRole sysRole = sysRoleMapper.selectOneById(roleId);
        return queryChain().select(SysDept::getDeptId)
            .from(SysDept.class)
            .leftJoin(SysRoleDept.class)
            .on(SysDeptTableDef.SYS_DEPT.DEPT_ID.eq(SysRoleDeptTableDef.SYS_ROLE_DEPT.DEPT_ID))
            .where(SysRoleDeptTableDef.SYS_ROLE_DEPT.ROLE_ID.eq(roleId))
            .and(SysDept::getDeptId).notIn(
                query().select(SysDeptTableDef.SYS_DEPT.PARENT_ID)
                    .from(SysDept.class)
                    .innerJoin(SysRoleDept.class)
                    .on(SysDeptTableDef.SYS_DEPT.DEPT_ID.eq(SysRoleDeptTableDef.SYS_ROLE_DEPT.DEPT_ID))
                    .and(SysRoleDeptTableDef.SYS_ROLE_DEPT.ROLE_ID.eq(roleId))
                , sysRole.getDeptCheckStrictly())
            .orderBy(SysDeptTableDef.SYS_DEPT.PARENT_ID, true)
            .orderBy(SysDeptTableDef.SYS_DEPT.CREATE_TIME, true)
            .listAs(Long.class);
    }

    /**
     * 查询部门下拉树结构
     *
     * @param sysDeptBo 实体
     * @return 查询结果
     */
    @Override
    public List<Tree<Long>> selectDeptTree(SysDeptBo sysDeptBo) {
        QueryWrapper query = buildQueryWrapper(sysDeptBo);
        query.eq(SysDept::getDeptStatus, UserConstants.DEPT_NORMAL);
        List<SysDeptVo> sysDeptVos = listAs(query, SysDeptVo.class);
        return buildDeptTreeSelect(sysDeptVos);
    }

    /**
     * 查询部门下拉树结构
     *
     * @param sysDeptVos 实体
     * @return 构造结果
     */
    @Override
    public List<Tree<Long>> buildDeptTreeSelect(List<SysDeptVo> sysDeptVos) {
        if (CollUtil.isEmpty(sysDeptVos)) {
            return CollUtil.newArrayList();
        }
        return TreeBuildUtils.build(sysDeptVos, (dept, tree) -> tree
            .setId(dept.getDeptId())
            .setParentId(dept.getParentId())
            .setName(dept.getDeptName())
            .setWeight(dept.getCreateTime()));
    }

    /**
     * 校验部门名称是否唯一
     *
     * @param sysDeptBo 实体
     * @return 校验结果
     */
    @Override
    public Boolean checkDeptNameUnique(SysDeptBo sysDeptBo) {
        return exists(query()
            .eq(SysDept::getDeptName, sysDeptBo.getDeptName())
            .eq(SysDept::getParentId, sysDeptBo.getParentId())
            .ne(SysDept::getDeptId, sysDeptBo.getDeptId(), ObjectUtil.isNotNull(sysDeptBo.getDeptId())));
    }

    /**
     * 查询所有子部门数(正常)
     *
     * @param deptId 部门ID
     * @return 查询结果
     */
    @Override
    public Long selectNormalChildrenByDeptId(Long deptId) {
        return count(query()
            .eq(SysDept::getDeptStatus, UserConstants.DEPT_NORMAL)
            .and(QueryMethods.findInSet(QueryMethods.string(deptId.toString()), SysDeptTableDef.SYS_DEPT.ANCESTORS).gt(0)));
    }

    /**
     * 查询部门是否存在用户
     *
     * @param deptId 部门ID
     * @return 查询结果
     */
    @Override
    public Boolean checkDeptExistUser(Long deptId) {
        return CollUtil.isNotEmpty(sysUserMapper.selectListByQuery(query().eq(SysUser::getDeptId, deptId)));
    }

    /**
     * 是否存在部门子节点
     *
     * @param deptId 部门ID
     * @return 查询结果
     */
    @Override
    public Boolean hasChildByDeptId(Long deptId) {
        return exists(query().eq(SysDept::getParentId, deptId));
    }

    /**
     * 通过部门ID查询部门名称
     *
     * @param deptIds 部门ID串逗号分隔
     * @return 部门名称串逗号分隔
     */
    @Override
    public String selectDeptNameByIds(String deptIds) {
        List<String> list = new ArrayList<>();
        for (Long id : StringUtils.splitTo(deptIds, Convert::toLong)) {
            SysDeptVo vo = SpringUtils.getAopProxy(this).queryById(id);
            if (ObjectUtil.isNotNull(vo)) {
                list.add(vo.getDeptName());
            }
        }
        return String.join(StringUtils.SEPARATOR, list);
    }

    /**
     * 通过部门ID查询部门
     *
     * @param deptIds 部门IDs
     * @return 部门列表
     */
    @Override
    public List<SysDeptVo> selectDeptByIds(List<Long> deptIds) {
        QueryWrapper query = query().select(SysDeptTableDef.SYS_DEPT.DEPT_ID, SysDeptTableDef.SYS_DEPT.DEPT_NAME, SysDeptTableDef.SYS_DEPT.LEADER)
            .eq(SysDept::getDeptStatus, UserConstants.DEPT_NORMAL)
            .in(SysDept::getDeptId, deptIds, CollUtil.isNotEmpty(deptIds));
        return listAs(query, SysDeptVo.class);
    }
}
